/**
 * Copyright (c) 2010, iDA MediaFoundry
 * All rights reserved.
 */
package be.idamf.cortizone.units
{
	import be.idamf.flash.utils.ObjectCopy;
	
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	
	import mx.events.FlexEvent;
	import mx.events.PropertyChangeEvent;

	/* TODO
	[Event(name="updateComplete", type="mx.events.FlexEvent")]
	[Event(name="conflict", type="flash.events.Event")]
	[Event(name="change", type="flash.events.Event")]
	*/	
	
	/**
	 * Watches an entity for changes.
	 * 
	 * @author Jan Van Coppenolle
	 */
	public class ObjectUnit extends EventDispatcher implements IUnitOfWork
	{
		////////////////////////////////////////////////////////////////////////
		// Constructor

		public function ObjectUnit(object:Object = null)
		{
			this.object = object;
		}

		////////////////////////////////////////////////////////////////////////
		// Properties

		private var _isDirty:Boolean;

		[Bindable]
		/**
		 * State of the object.
		 */
		public function get isDirty():Boolean
		{
			return _isDirty;
		}

		private function set isDirty(value:Boolean):void
		{
			_isDirty = value;
		}

		private var _object:Object;

		[Bindable]
		/**
		 * The object that is being watched.
		 */
		public function get object():Object
		{
			return _object;
		}

		public function set object(value:Object):void
		{
			if (_object)
				unwatch(_object);

			_object = value;

			if (_object)
				watch(_object);

			update();
		}

		private var _copy:Object;

		[Bindable]
		/**
		 * The object that is being watched.
		 */
		public function get copy():*
		{
			return _copy;
		}

		private function set copy(value:*):void
		{
			if (_copy)
				unwatch(_copy);

			_copy = value;

			if (_copy)
				watch(_copy);
		}

		////////////////////////////////////////////////////////////////////////
		// Methods

		/**
		 * Resets the watcher and possible rollback of changes.
		 */
		protected function update():void
		{
			isDirty = false;
			
			if (_object)
				copy = ObjectCopy.shallow(_object);
			else
				copy = null;
		}

		/**
		 * Commit changes.
		 */
		public function commit():void
		{
			if (!isDirty)
				return;

			unwatch(_object);
			ObjectCopy.migrate(_copy, _object);
			watch(_object);

			isDirty = false;
		}

		/**
		 * Revert changes by creating a new copy instance.
		 */
		public function rollback():void
		{
			update();
		}

		////////////////////////////////////////////////////////////////////////

		protected function watch(object:Object):void
		{
			var target:IEventDispatcher = object as IEventDispatcher;

			if (target == null)
				return;

			target.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, changeHandler, false, 0, true);
		}

		protected function unwatch(object:Object):void
		{
			var target:IEventDispatcher = object as IEventDispatcher;

			if (target == null)
				return;

			target.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, changeHandler);
		}

		////////////////////////////////////////////////////////////////////////
		// Handlers

		protected function changeHandler(event:PropertyChangeEvent):void
		{
			if (event.source == _copy)
			{
				isDirty = true;
				return;
			}

			if (event.source == _object)
			{
				if (_isDirty)
				{
					throw new Error("Conflict");
				}
				else
				{
					update();
				}
			}
		}
	}
}